/*
*   Calvin Neo
*   Copyright (C) 2016  Calvin Neo <calvinneo@calvinneo.com>
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License along
*   with this program; if not, write to the Free Software Foundation, Inc.,
*   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <string>
#include <vector>
#include <iostream>
#include "tokenizer.h"

struct ParseNode {
	void addpointer(ParseNode * ptrn, bool add_back = true);
	void addchild(const ParseNode & n, bool add_back = true);

	template <typename ... Args>
	void addlist(const ParseNode & x, Args&& ... args) {
		addchild(x);
		addlist(std::forward<Args>(args)...);
	}
	template <typename ... Args>
	void addlist(const ParseNode & x) {
		addchild(x);
	}

	template<typename Iterator>
	void addrange(Iterator begin, Iterator end) {
		for (Iterator iter = begin; iter != end; iter++)
		{
			addchild(**iter);
		}
	}

	void replace(int childid, const ParseNode & pn);
	inline ParseNode & get(int child_index) {
		return *(this->child[child_index]);
	}
	inline const ParseNode & get(int child_index) const {
		return *(this->child[child_index]);
	}
	inline const ParseNode & const_get(int child_index) const {
		return *(const_cast<const ParseNode *>(this)->child[child_index]);
	}

	void setattr(struct ParseAttr * pa);
	const std::string & to_string() const { return fs.CurrentTerm.what; }
	int length() const { return (int)child.size(); }
	TokenMeta_T & get_token() { return this->fs.CurrentTerm.token; }
	const TokenMeta_T & get_token() const { return this->fs.CurrentTerm.token; }
	std::string & get_what() { return this->fs.CurrentTerm.what; }
	const std::string & get_what() const { return this->fs.CurrentTerm.what; }
	template <typename ... Args>
	bool token_equals(const TokenMeta_T & token, Args&& ... args) const {
		return (this->get_token() == token) || token_equals(std::forward<Args>(args)...);
	}
	bool token_equals(const TokenMeta_T & token) const { return this->get_token() == token; };

	std::vector<ParseNode *>::iterator begin() { return child.begin(); }
	std::vector<ParseNode *>::iterator end() { return child.end(); }
	std::vector<ParseNode *>::const_iterator begin() const { return child.begin(); }
	std::vector<ParseNode *>::const_iterator end() const  { return child.end(); }

	ParseNode(const ParseNode &);
	//ParseNode(ParseNode &&) noexcept = delete; 
	ParseNode & operator= (const ParseNode &);
	ParseNode() : father(nullptr), attr(nullptr) {}; // -Wreorder
	ParseNode(const TokenizerState & s, ParseNode * fa, struct ParseAttr * att = nullptr) : fs(s) , father(fa), attr(att) {}
	~ParseNode();

public: // temporary
	TokenizerState fs;
	std::vector<ParseNode *> child;
	struct ParseNode * father = nullptr;
	struct ParseAttr * attr = nullptr;
};


int parse(std::string code);
void preorder(ParseNode * ptree);
TokenizerState & get_tokenizer_state();
void print_error(const std::string & error_info, const ParseNode & node);
void print_error(const std::string & error_info);
void fatal_error(const std::string & error_info, const ParseNode & node);
void fatal_error(const std::string & error_info);

// yacc part code
// implement in for90.y
#define USE_TRIVIAL
#define USE_REUSE

#if defined USE_TRIVIAL
#define YYSTYPE ParseNode*
// X must be a newly-created ParseNode generated by `gen_` functions
// whose all childs are copied from arguments which are passed to `gen_` in the form of `ARG_IN`
#define RETURN_NT(X) new ParseNode(X)
inline ParseNode & YY2ARG(YYSTYPE X) {
	// #define YY2ARG (X == nullptr? ParseNode(): *X)
	if (X == nullptr)
	{
		fatal_error("null ParseNode during parsing");
	}
	else {
		return *X;
	}
}
#define ARG_IN const ParseNode &
#define ARG_OUT ParseNode &
template <typename ... Args>
void CLEAN_DELETE(YYSTYPE & x, Args&& ... args) {
	// for others, clear, because it's not needed as child
	delete x;
	x = nullptr;
	CLEAN_DELETE(std::forward<Args>(args)...);
}
template <typename ... Args>
void CLEAN_DELETE(YYSTYPE & x) {
	delete x;
	x = nullptr;
}
template <typename ... Args>
void CLEAN_REUSE(YYSTYPE & x, Args&& ... args) {
	// for some, do not clear, reuse as child
	CLEAN_REUSE(std::forward<Args>(args)...);
}
template <typename ... Args>
void CLEAN_REUSE(YYSTYPE & x) {

}
#elif defined USE_POINTER
#define YYSTYPE ParseNode*
#define RETURN_NT(X) new ParseNode(X)
inline ParseNode & YY2ARG(YYSTYPE X) {
	// #define YY2ARG (X == nullptr? ParseNode(): *X)
	if (X == nullptr)
	{
		fatal_error("null ParseNode during parsing");
		return ParseNode{};
	}
	else {
		return *X;
	}
}
#define ARG_IN const ParseNode &
#define ARG_OUT ParseNode &
template <typename ... Args>
void CLEAN_DELETE(YYSTYPE & x, Args&& ... args) {
}
template <typename ... Args>
void CLEAN_DELETE(YYSTYPE & x) {
}
template <typename ... Args>
void CLEAN_REUSE(YYSTYPE & x, Args&& ... args) {
	CLEAN_REUSE(std::forward<Args>(args)...);
}
template <typename ... Args>
void CLEAN_REUSE(YYSTYPE & x) {

}
#else // use RAII
#define YYSTYPE ParseNode
#define RETURN_NT(X) X
#define YY2ARG(X) X
#define ARG_IN const ParseNode &
#define ARG_OUT ParseNode &
template <typename ... Args>
void CLEAN_DELETE(YYSTYPE & x, Args&& ... args) {
	// do nothing
}
template <typename ... Args>
void CLEAN_DELETE(YYSTYPE & x) {
	// do nothing
}
template <typename ... Args>
void CLEAN_REUSE(YYSTYPE & x, Args&& ... args) {
	// do nothing
	CLEAN_REUSE(std::forward<Args>(args)...);
}
template <typename ... Args>
void CLEAN_REUSE(YYSTYPE & x) {
	// do nothing
}
#endif

std::string tabber(const std::string &, bool tail_crlf = true); // add tab(`\t`) into the front of each line
ParseNode flatten_bin(const ParseNode & pn, bool recursion_direction_right);// eliminate left/right recursion of an binary tree
void flatten_bin_inplace(ParseNode & pn, bool recursion_direction_right); // eliminate left/right recursion of an binary tree in place


/***************
* by testing 
* a 202kb code file will cause `sprintf` erase data elsewhere(when `MAX_CODE_LENGTH` is 32767)
* `MAX_CODE_LENGTH` should be enough
***************/
#define MAX_CODE_LENGTH 262143

struct ParseConfig {
	bool for90 = true;
	bool isdebug = false;
	/***************
	* source code from file/stdin
	***************/
	bool hasfile = false;
	bool usefor = true;
	bool usefarray = true;
	/***************
	* set true to add full qualifier for fortran's intrinsic functions
	* e.g.
	* for90std::forwritefree(...)
	***************/
	bool full_quali = false;
};


#ifdef _DEBUG
#define WHENDEBUG(THEN, ELSE) THEN
#else
#define WHENDEBUG(THEN, ELSE) ELSE
#endif // _DEBUG
